<template>
  <div class="m-upload">
    <el-upload
      v-bind="$attrs"
      :multiple="props.multiple"
      :accept="props.accept"
      :list-type="props.listType"
      :disabled="disabled"
      :file-list="fileList"
      :on-preview="onPreview"
      :on-remove="onRemove"
      :on-change="onChange"
      :before-upload="beforeUpload"
      :http-request="httpRequest"
    >
      <slot :disabled="disabled">
        <el-button
          v-if="props.listType !== 'picture-card'"
          :disabled="disabled"
          type="primary"
        >
          <el-icon><UploadFilled /></el-icon> &nbsp; {{ clickText }}</el-button
        >
        <m-icon v-else name="icon-plus"></m-icon>
      </slot>
      <template #tip>
        <slot name="tip"></slot>
      </template>
    </el-upload>
  </div>
</template>

<script setup lang="ts">
import type { UploadProps as ElUploadProps } from 'element-plus'
import type { UploadFile, UploadRule } from './upload'
import { uploadProps, uploadEmits } from './upload'
import { ref, watch, computed, reactive } from 'vue'
import { ElMessage } from 'element-plus'
import { api as viewerApi } from 'v-viewer'
import { joinBaseUrlFile } from '@/utils'
import { UploadFilled } from '@element-plus/icons-vue'
import { isImage, useProgressFake } from '@/utils'
import { cloneDeep } from 'lodash-es'

defineOptions({
  name: 'MUpload',
  inheritAttrs: false
})

const props = defineProps(uploadProps)
const emit = defineEmits(uploadEmits)

const fileList = ref<UploadFile[]>([])

watch(
  () => props.modelValue,
  (val) => {
    if (fileList.value === val) {
      return
    }
    const modelValue = cloneDeep(val)
    fileList.value = modelValue.map((item: any) => ({
      ...item,
      url: joinBaseUrlFile(item.url),
      response:
        {
          filename: item.name || '',
          url: item.url || '',
          ...item.response
        } || {}
    }))
  },
  {
    immediate: true,
    deep: true
  }
)

watch(
  () => fileList.value,
  (val) => {
    emit('update:modelValue', val)
    emit('change', val)
  },
  {
    immediate: true
  }
)

const onPreview: ElUploadProps['onPreview'] = (uploadFile) => {
  if (!props.preview) {
    return
  }

  // onPreview
  if (props.preview !== true) {
    props.preview(uploadFile as UploadFile)
    return
  }

  const initialViewIndex = fileList.value.findIndex(
    (file) => file.uid === uploadFile.uid
  )
  // console.log(fileList.value, 'fileList.value')

  if (!isImage(fileList.value[0].url)) {
    window.open(fileList.value[0].url, '_blank')
    // downloadFile(fileList.value[0].name, fileList.value[0].url)
  } else {
    viewerApi({
      // TODO: https://github.com/fengyuanchen/viewerjs
      options: {
        toolbar: true,
        url: 'url',
        initialViewIndex
      },
      images: [fileList.value?.[initialViewIndex]?.url]
    })
  }
}

const onRemove: ElUploadProps['onRemove'] = (uploadFile, uploadFiles) => {
  fileList.value = uploadFiles as UploadFile[]
}

const isMax = computed(() => fileList.value.length >= props.max)
const disabled = computed(() => props.disabled)

const beforeUpload: ElUploadProps['beforeUpload'] = (rawFile) => {
  const rules: UploadRule[] = [
    {
      validator: () => isMax.value,
      message: `最多上传${props.max}个文件`
    },
    {
      validator: (file) => {
        if (props.accept === 'all') {
          return false
        }
        const fileType = file.type.split('/')[1]
        return !props.accept.includes(fileType)
      },
      message: '文件格式不符合要求'
    },
    {
      validator: (file) => file.size > props.maxSize,
      message: `文件不能超过${props.maxSize}byte`
    },
    ...props.rules
  ]

  const rule = rules.find((rule) => rule.validator(rawFile))
  if (rule) {
    ElMessage.warning(rule.message)
    return false
  }
}

const onChange = () => {
  if (props.removeFail) {
    fileList.value = fileList.value.filter((file) => file.status !== 'fail')
  } else {
    fileList.value = [...fileList.value]
  }
}

const httpRequest: ElUploadProps['httpRequest'] = (options) => {
  const { file } = options
  // 当前文件信息
  const fileListItem = reactive<UploadFile>({
    name: file.name,
    url: URL.createObjectURL(file),
    status: 'uploading',
    percentage: 0
  })

  fileList.value.push(fileListItem)

  // 进度条
  const { cancel } = useProgressFake(
    (percent) => {
      fileListItem.percentage = percent
    },
    {
      interval: 100,
      maxNum: 99,
      increaseNum: 3
    }
  )

  // 请求上传接口
  const formdata = new FormData()
  formdata.append(props.httpFileKey, file)

  return props
    .http(formdata)
    .then(({ data }: any) => {
      fileListItem.name = data.filename
      fileListItem.url = joinBaseUrlFile(data.url)
      fileListItem.status = 'success'
      fileListItem.response = data
      if (props.showMessage) {
        ElMessage.success('上传成功')
      }
    })
    .catch(() => {
      fileListItem.status = 'fail'
      if (props.showMessage) {
        ElMessage.error('上传失败')
      }
    })
    .finally(cancel)
}

const downloadFile = (name: string, url: string) => {
  const a = document.createElement('a')

  const rootDocument = window.document

  // void 0 相当于 undefined
  if (a.download !== void 0) {
    // 在新窗口打开
    a.target = '_blank'
    a.style.display = 'none'
    // 为 a 设置属性 href
    a.setAttribute('href', url)
    // 为 a 设置 download 属性
    a.setAttribute('download', name)
    rootDocument.body.appendChild(a)
    a.dispatchEvent(new MouseEvent('click'))
    rootDocument.body.removeChild(a)

    // setTimeout(() => {
    //   URL.revokeObjectURL(url);
    // }, 100);
  }
}
</script>

<style lang="scss" scoped>
.m-upload {
  width: 100%;
}
</style>
